package society.framework.core;

import java.io.Serializable;

import society.framework.core.communication.Message;


/**
 * List used to handle messages. It simulates a queue where adding is done only by one thread at a time.
 * Removing from the list can block if there is no Message yet.
 * Methods are synchronized and removing from the list can block execution until there is something to remove or the timer ends.
 * @author Micu Andrei
 *
 */
public class MessageList implements Serializable{
	private static final long serialVersionUID = -8411485143334552910L;
	protected RemoveLock removeLock = new RemoveLock();
	
	protected class RemoveLock implements Serializable {
		private static final long serialVersionUID = -7834729667731878541L;
	}

	protected class Node implements Serializable{
		private static final long serialVersionUID = -8306109335882571440L;
		Message message;
		Node next;
		Node(Message message, Node next){
			this.message=message;
			this.next=next;
		}
	}
	
	protected Node first=null;
	protected Node last=null;
	
	/**
	 * Adds a message at the end of the queue
	 * @param msg - the Message to add
	 */
	public synchronized void add(Message msg){
		if(first==null){
			first = last = new Node(msg, null);		//verifies the first element which is sensitive
		} else {
			last.next = new Node(msg, null);
			last = last.next;
		}
		synchronized(removeLock){
			removeLock.notify();					//notifies one of the consumer threads that an element has been added
		}
	}
	
	/**
	 * Returns the element from the head of the queue. It blocks if the list is empty until something is
	 * added to the list or the removing is cancelled by cancelRemoving() method.
	 * @return - the Message at the head of the queue
	 */
	public Message remove() {
		synchronized(removeLock){
			try {
				if (first == null){
					removeLock.wait();
					if (first == null) return null;
				}
				Node q=first;
				first=first.next;
				return q.message;
			} catch (InterruptedException e) {
				return null;
			}
		}
	}
	
	/**
	 * Returns the element from the head of the queue. It blocks if the list is empty until there is
	 * something to return or until the timeout expires.
	 * @param timer - the amount of milliseconds to wait until the function returns
	 * @return - the Message at the head of the queue or null if the timeout expired
	 */
	public Message remove(int timer){
		synchronized(removeLock){
			try {
				if (first == null){
					removeLock.wait(timer);
					if (first == null) return null;
				}
				Node q=first;
				first=first.next;
				return q.message;
			} catch (InterruptedException e) {
				return null;
			}
		}
	}
	
	/**
	 * Notifies the thread that waits to remove an element from the list to unblock it.
	 */
	public void cancelRemoving(){
		synchronized(removeLock){
			removeLock.notify();
		}
	}

}
